package com.microframework.base.core.datasource.aop;

import com.jfinal.plugin.activerecord.ActiveRecordException;
import com.jfinal.plugin.activerecord.Config;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.NestedTransactionHelpException;
import com.jfinal.plugin.activerecord.tx.TxConfig;
import com.microframework.base.core.datasource.annotation.Tx;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Savepoint;
import java.sql.SQLException;
import java.util.concurrent.*;

/**
 * JFinal事务AOP处理器
 * 拦截带有@Tx注解的方法，提供事务管理能力，支持嵌套事务和事务隔离级别控制
 */
@Slf4j
@Aspect
@Component
@Order(1) // 确保事务切面优先级高于其他切面
public class JFinalTxAop {

    // 用于超时控制的线程池
    private static final ExecutorService timeoutExecutor = Executors.newCachedThreadPool();

    /**
     * 定义切点：拦截带有@Tx注解的方法
     */
    @Pointcut("@annotation(com.microframework.base.core.datasource.annotation.Tx)")
    private void txMethods() {
        // 切点定义，不需要实现
    }

    /**
     * 环绕通知，处理事务逻辑
     * @param pjp 连接点
     * @return 方法执行结果
     * @throws Throwable 执行异常
     */
    @Around("txMethods()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        // 获取方法上的Tx注解
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();
        Tx tx = method.getAnnotation(Tx.class);
        
        // 获取事务配置
        Config config = getConfigWithTxConfig(pjp);
        if (config == null) {
            config = DbKit.getConfig();
        }

        // 检查是否已存在事务连接（处理嵌套事务情况）
        Connection conn = config.getThreadLocalConnection();
        if (conn != null) {
            return handleNestedTransaction(pjp, config, conn, tx);
        }

        // 创建新事务
        return handleNewTransaction(pjp, config, tx);
    }

    /**
     * 处理嵌套事务
     * @param pjp 连接点
     * @param config 数据库配置
     * @param conn 当前数据库连接
     * @param tx 事务注解
     * @return 方法执行结果
     * @throws Throwable 执行异常
     */
    private Object handleNestedTransaction(ProceedingJoinPoint pjp, Config config, 
                                          Connection conn, Tx tx) throws Throwable {
        // 1. 保存当前事务状态
        boolean originalAutoCommit = conn.getAutoCommit();
        int originalIsolation = conn.getTransactionIsolation();
        
        // 2. 设置嵌套事务隔离级别
        int requiredLevel = getTransactionLevel(config);
        if (conn.getTransactionIsolation() != requiredLevel) {
            try {
                conn.setTransactionIsolation(requiredLevel);
            } catch (SQLException e) {
                log.warn("设置事务隔离级别失败: {}", e.getMessage());
            }
        }
        
        // 3. 创建保存点
        Savepoint savepoint = null;
        boolean useSavepoint = tx == null || tx.useSavepoint();
        if (useSavepoint) {
            try {
                savepoint = conn.setSavepoint();
                log.debug("创建保存点: {}", savepoint);
            } catch (SQLException e) {
                log.error("创建保存点失败", e);
                throw new ActiveRecordException("无法创建嵌套事务保存点", e);
            }
        }
        
        try {
            Object result;
            // 4. 执行带超时控制的方法（如果配置了超时）
            if (tx != null && tx.timeout() > 0) {
                result = executeWithTimeout(pjp, tx.timeout());
            } else {
                result = pjp.proceed();
            }
            
            // 5. 释放保存点
            if (savepoint != null) {
                try {
                    conn.releaseSavepoint(savepoint);
                } catch (SQLException e) {
                    log.warn("释放保存点失败", e);
                }
            }
            return result;
        } catch (Throwable t) {
            // 6. 嵌套事务回滚处理
            if (savepoint != null && shouldRollback(t, tx)) {
                try {
                    log.debug("回滚到保存点: {}", savepoint);
                    conn.rollback(savepoint);
                } catch (SQLException e) {
                    log.error("回滚到保存点失败", e);
                    throw new ActiveRecordException("嵌套事务回滚失败", e);
                }
            } else if (savepoint == null) {
                // 7. 标记外层事务需要回滚
                throw new NestedTransactionHelpException("嵌套事务异常");
            }
            throw t;
        } finally {
            // 8. 恢复原始事务设置
            try {
                if (conn.getTransactionIsolation() != originalIsolation) {
                    conn.setTransactionIsolation(originalIsolation);
                }
                if (conn.getAutoCommit() != originalAutoCommit) {
                    conn.setAutoCommit(originalAutoCommit);
                }
            } catch (SQLException e) {
                log.error("恢复事务设置失败", e);
            }
        }
    }

    /**
     * 处理新事务
     * @param pjp 连接点
     * @param config 数据库配置
     * @param tx 事务注解
     * @return 方法执行结果
     * @throws Throwable 执行异常
     */
    private Object handleNewTransaction(ProceedingJoinPoint pjp, Config config, Tx tx) throws Throwable {
        Connection conn = null;
        Boolean originalAutoCommit = null;
        
        try {
            log.debug("开始事务: {}", config.getName());
            // 1. 获取数据库连接
            conn = config.getConnection();
            originalAutoCommit = conn.getAutoCommit();
            // 2. 绑定连接到当前线程
            config.setThreadLocalConnection(conn);
            
            // 3. 设置事务参数
            conn.setTransactionIsolation(getTransactionLevel(config));
            conn.setAutoCommit(false);
            
            // 4. 执行带超时控制的方法
            Object result;
            if (tx != null && tx.timeout() > 0) {
                result = executeWithTimeout(pjp, tx.timeout());
            } else {
                result = pjp.proceed();
            }
            
            // 5. 提交事务
            conn.commit();
            log.debug("提交事务: {}", config.getName());
            return result;
        } catch (TimeoutException e) {
            // 6. 处理超时异常
            rollbackQuietly(conn);
            log.error("事务超时: {}", config.getName(), e);
            throw new RuntimeException("事务执行超时", e);
        } catch (Throwable t) {
            // 7. 根据回滚规则处理异常
            if (shouldRollback(t, tx)) {
                rollbackQuietly(conn);
                log.debug("事务回滚: {}", config.getName(), t);
            } else {
                try {
                    if (conn != null && !conn.isClosed()) {
                        conn.commit();
                        log.debug("异常提交事务: {}", config.getName());
                    }
                } catch (SQLException e) {
                    log.error("提交事务失败", e);
                }
            }
            throw t instanceof RuntimeException ? (RuntimeException) t : new ActiveRecordException(t);
        } finally {
            // 8. 清理连接资源
            closeConnection(conn, originalAutoCommit, config);
        }
    }
    
    /**
     * 判断是否需要回滚事务
     * @param t 抛出的异常
     * @param tx 事务注解配置
     * @return 是否需要回滚
     */
    private boolean shouldRollback(Throwable t, Tx tx) {
        // 默认回滚所有异常
        if (tx == null) return true;
        
        Class<? extends Throwable>[] noRollbackFor = tx.noRollbackFor();
        Class<? extends Throwable>[] rollbackFor = tx.rollbackFor();
        
        // 1. 检查noRollbackFor配置
        if (noRollbackFor != null && noRollbackFor.length > 0) {
            for (Class<? extends Throwable> cls : noRollbackFor) {
                if (cls.isInstance(t)) {
                    return false; // 匹配到不回滚的异常
                }
            }
        }
        
        // 2. 检查rollbackFor配置
        if (rollbackFor.length > 0) {
            for (Class<? extends Throwable> cls : rollbackFor) {
                if (cls.isInstance(t)) {
                    return true; // 匹配到需要回滚的异常
                }
            }
            return false; // 未匹配到需要回滚的异常
        }
        
        // 3. 默认回滚RuntimeException和Error
        return (t instanceof RuntimeException || t instanceof Error);
    }

    /**
     * 带超时控制的方法执行
     * @param pjp 连接点
     * @param timeoutSeconds 超时时间（秒）
     * @return 方法执行结果
     * @throws Throwable 执行异常
     */
    private Object executeWithTimeout(final ProceedingJoinPoint pjp, int timeoutSeconds) 
        throws Throwable {
        
        Future<Object> future = timeoutExecutor.submit(() -> {
            try {
                return pjp.proceed();
            } catch (Throwable e) {
                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new ExecutionException(e);
                }
            }
        });
        
        try {
            return future.get(timeoutSeconds, TimeUnit.SECONDS);
        } catch (ExecutionException e) {
            // 解包原始异常
            Throwable cause = e.getCause();
            if (cause != null) throw cause;
            throw e;
        } catch (TimeoutException e) {
            // 取消任务并抛出超时异常
            future.cancel(true);
            throw e;
        }
    }

    /**
     * 安静地回滚事务
     * @param conn 数据库连接
     */
    private void rollbackQuietly(Connection conn) {
        if (conn != null) {
            try {
                if (!conn.isClosed()) {
                    conn.rollback();
                    log.debug("事务已回滚");
                }
            } catch (SQLException e) {
                log.error("回滚事务失败", e);
            }
        }
    }

    /**
     * 安全关闭连接
     * @param conn 数据库连接
     * @param autoCommit 原始自动提交状态
     * @param config 数据库配置
     */
    private void closeConnection(Connection conn, Boolean autoCommit, Config config) {
        if (conn == null) return;
        
        try {
            // 1. 从线程上下文中移除连接
            config.removeThreadLocalConnection();
            
            // 2. 恢复原始自动提交状态
            if (autoCommit != null && conn.getAutoCommit() != autoCommit) {
                conn.setAutoCommit(autoCommit);
            }
            
            // 3. 关闭连接
            if (!conn.isClosed()) {
                conn.close();
                log.debug("数据库连接已关闭");
            }
        } catch (Throwable t) {
            log.error("关闭连接失败", t);
        }
    }

    /**
     * 获取配置的事务隔离级别
     * @param config 数据库配置
     * @return 事务隔离级别
     */
    protected int getTransactionLevel(Config config) {
        return config.getTransactionLevel();
    }

    /**
     * 获取TxConfig注解配置的数据源
     * @param pjp 连接点
     * @return 数据库配置
     */
    private static Config getConfigWithTxConfig(ProceedingJoinPoint pjp) {
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();
        
        // 1. 先从方法上获取TxConfig注解
        TxConfig txConfig = method.getAnnotation(TxConfig.class);
        
        // 2. 如果方法上没有，则从类上获取
        if (txConfig == null) {
            txConfig = pjp.getTarget().getClass().getAnnotation(TxConfig.class);
        }

        // 3. 如果找到了TxConfig注解，根据其值获取对应的Config
        if (txConfig != null) {
            String configName = txConfig.value();
            Config config = DbKit.getConfig(configName);
            if (config == null) {
                throw new RuntimeException("数据源配置未找到: " + configName);
            }
            return config;
        }
        
        // 4. 获取方法上的Tx注解，看是否指定了数据源
        Tx tx = method.getAnnotation(Tx.class);
        if (tx != null && !tx.value().isEmpty()) {
            String configName = tx.value();
            Config config = DbKit.getConfig(configName);
            if (config == null) {
                throw new RuntimeException("数据源配置未找到: " + configName);
            }
            return config;
        }
        
        return null;
    }
}